/*******************************************************************************
 * File name: 		uart.c
 * Description: 	uart driver
 * Project: 		uart
 * Target: 			LPC2478
 * Compiler: 		arm-none-eabi-gcc
 * Date: 			09-09-2011
 * Author: 			kuba
 * Based on: 		---
 ******************************************************************************/

/*==============================================================================
 Includes
==============================================================================*/
# include "LPC24xx.h"
# include "init.h"
# include "irq.h"

# include "uart.h"
# if UART_TEST
# include "board.h"
# endif
/*==============================================================================
 Defines
==============================================================================*/
# define UART_BASE_DIV			16
# define FIFO_SIZE				16
# define CHARS_NUM_IN_FIFO		4
/*==============================================================================
 Globals
==============================================================================*/
extern struct uart u0;
# if UART_TEST
//static volatile uint32 x = LED1;
# endif
volatile bool uart0TxEmpty = FALSE;		/* if true, it denotes TX buffer empty */
volatile uint8 uart0Status;				/* stores error bits */
/*==============================================================================
 Static ISR prototypes
==============================================================================*/
static void uart0Isr (void) __attribute__ ((interrupt("IRQ")));
/*==============================================================================
 Static function prototypes
==============================================================================*/

/*==============================================================================
 Global function definitions
==============================================================================*/
/*------------------------------------------------------------------------------
 function name:	uartInit
 description:		uart initialization
 parameters:		pointer to const uart struct
 returned value:	true or false
------------------------------------------------------------------------------*/
bool uartInit (const struct uart *uart)
{
	uint32 fDiv;

	switch (uart->uartNo)
	{
	case uart0:
		/* UART0 is enabled on reset- no PCONP write */
		fDiv = (72000000 / UART_BASE_DIV) / (uart->baud);
		/* at P0[2],[3] there are TXD0, RXD0; PINSEL0 5:4 and 7:6 are 01 (0x50) */
		PINSEL0 = (PINSEL0_TXD0 | PINSEL0_RXD0);
		/* set here: 8 bit character, 1 bit stop, none parity,
		 * set DLAB to enable access to divisor latches- DLL and DLM are accessible */
		U0LCR = ((uart->wordLen) | (uart->stopBit) | (uart->parity) | (uart->paritySel)
					| (uart->breakCtrl) | U_LCR_DLAB);
		/* set divisor latches DLL, DLM */
		U0DLM = fDiv / 256;
		U0DLL = fDiv % 256;
		/* clear DLAB to disable DLL, DLM- now RBR, THR and IER are accessible */
		U0LCR &= ~(U_LCR_DLAB);
		if (uart->intrpt)
		{
			if (irqInit(irqUART0, (void *) uart0Isr, irqLOWEST_PRIORITY) == FALSE)
			{
				return (FALSE);
			}
		}

		/* set fifo control register */
		/* NOTE: it must be written in one step */
		switch (uart->intrptRxTrig)
		{
		case intrptRX_TRIG_1CHAR:
		   U0FCR = ((uart->fifo) | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX | U_FCR_RX_FIFO_TRIGGER_LEVEL_1CHAR);
		   break;

		case intrptRX_TRIG_4CHAR:
		   U0FCR = ((uart->fifo) | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX | U_FCR_RX_FIFO_TRIGGER_LEVEL_4CHAR);
		   break;

		case intrptRX_TRIG_8CHAR:
		   U0FCR = ((uart->fifo) | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX | U_FCR_RX_FIFO_TRIGGER_LEVEL_8CHAR);
		   break;

		case intrptRX_TRIG_14CHAR:
		   U0FCR = ((uart->fifo) | U_FCR_FIFO_RST_RX | U_FCR_FIFO_RST_TX | U_FCR_RX_FIFO_TRIGGER_LEVEL_14CHAR);
		   break;

		default:
		   return (FALSE);
		}

		U0IER = uart->intrpt;
		break;	/* end of uart0 */

	case uart1:
		break;	/* end of uart1 */

	case uart2:
      fDiv = (72000000 / UART_BASE_DIV) / (uart->baud);
      PCONP  |= (1 << 24);  // wlacz uart 2
      //PCLKSEL1 |= PCLKSEL1_UART2_FREQ_DIV1;   // max. speed
      /* at P0[2],[3] there are TXD0, RXD0; PINSEL0 5:4 and 7:6 are 01 (0x50) */
      PINSEL0 |= (PINSEL0_TXD2 | PINSEL0_RXD2);
      /* set here: 8 bit character, 1 bit stop, none parity,
       * set DLAB to enable access to divisor latches- DLL and DLM are accessible */
      U2LCR = ((uart->wordLen) | (uart->stopBit) | (uart->parity) | (uart->paritySel)
               | (uart->breakCtrl) | U_LCR_DLAB);
      /* set divisor latches DLL, DLM */
      U2DLM = fDiv / 256;
      U2DLL = fDiv % 256;
      /* clear DLAB to disable DLL, DLM- now RBR, THR and IER are accessible */
      U2LCR &= ~(U_LCR_DLAB);
		break;	/* end of uart2 */

	case uart3:
		break;	/* end of uart3 */

	default:		/* wrong uart number */
		return (FALSE);
	}
	return (TRUE);
}

/*------------------------------------------------------------------------------
 function name: 	uartSend
 description:		sends data to the UART from buffer. There is no hardware
                  tigger level for TX, but it is implemented. It takes advantage of the fact
                  that TX fifo can be constantly filled with characters and if filling ends,
                  then hardware generate interrupts. So, here software pushes required number
                  of chars and waits- during the delay TX interrupt occurs. Take a look
                  at uartSend function.
 parameters:		pointer to const uart struct
 returned value:	none
------------------------------------------------------------------------------*/
bool uartSend	(const struct uart *uart)
{
	uint32 len = uart->buffers.txBuffSize;
	uint8 *p = uart->buffers.txBuff;
	uint8 chars = 0;

	switch (uart->uartNo)
	{
	case uart0:
		while (len)
		{
			while (uart0TxEmpty == FALSE);	/* wait if TX buffer is not empty */
			U0THR = *(p++);						/* write char to THR and increment pointer */
			--len;
			++chars;
			if (chars >= uart->intrptTxTrig)
			{
				chars = 0;
				uart0TxEmpty = FALSE;			/* full set of chars in fifo */
			}
		}
		break;	/* end of uart0 */

	case uart1:
		break;	/* end of uart1 */

	case uart2:
		break;	/* end of uart2 */

	case uart3:
		break;	/* end of uart3 */

	default:		/* wrong uart number */
		return (FALSE);

	}
	return (TRUE);
}

/*------------------------------------------------------------------------------
 function name:	uartPutChar
 description:		function used by uprintf(), sends a char to the uart
 parameters:		character to send
 returned value:	none
------------------------------------------------------------------------------*/
void uartPutChar (char character)
{
	while (!(U2LSR & U_LSR_THR_EMPTY));
	U2THR = character;
}

/*------------------------------------------------------------------------------
 function name:	uartGetChar
 description:		get a character from the UART register
 parameters:		none
 returned value:	received character
------------------------------------------------------------------------------*/
uint8 uartGetChar (void)
{
	while (!(U0LSR & U_LSR_THR_EMPTY));
	return (U0RBR);
}
/*==============================================================================
 Static function definitions
==============================================================================*/

/*==============================================================================
 Static ISR definitions
==============================================================================*/
static void uart0Isr (void)
{
	static uint32 cnt;	/* index for uart0 buffer */
	uint8 lsr; 			/* get Line Status Register */
	uint32 i;
	/* get Interrupt Identification Register- U0IIR to check interrupt status */
	uint32 reg = U0IIR;
	/* mask bits 1-3 to identify interrupt source, skip pending interrupt bit */
	reg &= U_IIR_INTERRUPT_ID_mask;

	/* check what interrupt happened */
	/* if Receive Data Available- RDA interrupt */
	if (reg == U_IIR_INTERRUPT_ID_RDA)
	{
# if UART_TEST
	   //LED0_PIN ^= x;
# endif
		/* get iterations number by shifting triger level by its bit position */
		for (i = 0; i < u0.intrptRxTrig; ++i)
		{
			if (cnt >= u0.buffers.rxBuffSize)	/* buffer overflow? */
			{
				cnt = 0;									/* no way */
			}
			u0.buffers.rxBuff[cnt++] = U0RBR;	/* save data in buffer & increment index */
		}
	}

	/* if Character Timeout Indicator - CTI */
	if (reg == U_IIR_INTERRUPT_ID_CTI)
	{
		if (cnt >= u0.buffers.rxBuffSize)	/* buffer overflow? */
		{
			cnt = 0;									/* no way */
		}
		u0.buffers.rxBuff[cnt++] = U0RBR;	/* save data in buffer & increment index */
	}

	/* if Transmit Holding Register Empty- THRE */
	if (reg == U_IIR_INTERRUPT_ID_THRE)
	{
		uart0TxEmpty = TRUE;					/* set flag TX buffer empty */
	}

	/* if Receive Line Status changed... */
	if (reg == U_IIR_INTERRUPT_ID_RLS)
	{
		/* get Line Status Register U0LSR */
		lsr = U0LSR;
		if (lsr & (U_LSR_OVERRUN_ERROR | U_LSR_PARITY_ERROR | U_LSR_FRAMING_ERROR
					| U_LSR_BREAK_INTERRUPT | U_LSR_RX_FIFO_ERROR))
		{
			uart0Status = lsr;			/* save error bits to the global variable */
			reg = U0RBR;					/* read RBR to clear the interrupt */
		}
		/* what about RDR? we get data before? */
	}

	VICVectAddr = 0;
}
/******************************************************************************
* END OF FILE
******************************************************************************/




